import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JOptionPane;

import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleWeightedGraph;

import cytoscape.CyEdge;
import cytoscape.CyNetwork;
import cytoscape.CyNode;
import cytoscape.Cytoscape;
import cytoscape.task.Task;
import cytoscape.task.TaskMonitor;
import cytoscape.task.ui.JTaskConfig;


/********************************************************************************
 *  This is the main class of the enrichment plugin.
 *  It makes the clustering, creates a new network in cytoscape based on the
 *  results of the clustering and writes the results to the output file. 
 * @author liatperl
 * Last updater: Liat
 * Date : 20.8.09
 ********************************************************************************/
public class PluginCore implements Task {

	private String _outputFile;
	private double _cutoff;
	private CyNetwork _cyNet;
	private double _alpha;
	private int _algorithm;
	private TaskMonitor _taskMonitor;
	public static int MCL = 1;
	public static int NETWORK_BLAST = 2;
	private boolean interrupted;

	private String INITIAL_STATUS = "Computing GO enriched clusters...";
	private String GO_SIMILAR = "Calculating proteins' GO similary";
	private String GENERATE_CLUSTERS = "Generating clusters";
	private String ASSIGN_ANNOT = "Assigning annotations to clusters";
	private static final String DONE_MESSAGE = "Done.";


	/**
	 * Constructor.
	 * @param outputFile - the output file on which the results will be written
	 * @param cutoff the cutoff for the clusters.
	 * @param alpha the relative weight given to the GO similarity.
	 * @param algorithm - the clustering method can be either mcl or networkblast.
	 */
	public PluginCore(String outputFile, double cutoff, double alpha,int algorithm) {
		_outputFile = outputFile;
		_cutoff = cutoff;
		_cyNet = Cytoscape.getCurrentNetwork();
		_alpha = alpha;
		_algorithm = algorithm;
		interrupted = false;
		// initialize config.

		/*
		LinkedList<Cluster> list = new LinkedList<Cluster>();
		Iterator<CyNode> nodesIter = _cyNet.nodesIterator();
		int ind = 0;
		CyNode curNode;
		Node[] nodes = new Node[5];
		while (nodesIter.hasNext()) {
			curNode = nodesIter.next();
			nodes[ind] = new Node(curNode.getIdentifier(), curNode.getRootGraphIndex());
			ind++;
		}
		Cluster clus1 = new Cluster();
		clus1.add(nodes[0]); clus1.add(nodes[1]); clus1.add(nodes[2]);
		Cluster clus2 = new Cluster();
		clus2.add(nodes[3]); clus2.add(nodes[4]);
		list.add(clus1);
		list.add(clus2);
		 */
	}


	/**
	 * Adds annotations to the given clusters.
	 * @param clusters the clusters to add the annotation.
	 * @param annotatedGenesInGenome number of annotated genes in the genome.
	 */
	private void addAnnotToClusters(LinkedList<Cluster> clusters, int annotatedGenesInGenome) {
		TermFinder gtf = new TermFinder(0.05, annotatedGenesInGenome, 
				"termFinder.pl", "gene_ontology_edit.obo", "gene_assoc_human.txt" );
		gtf.find(clusters);

		System.out.println("AFTER GO TERM FINDER AND FILTERING NON ANNOTATED CLUSTERS:");

		Iterator<Cluster> citer = clusters.iterator();
		while (citer.hasNext()) {
			Cluster c = citer.next();
			if (c.isAnnotated()) {
				//	System.out.println(c.getAnnotation() + "\t" +  c.toString());	
			}
			else {
				c.clear();
				citer.remove();
			}
		}

	}

	/**
	 * This function calls the clustring algorithms,
	 * Calls the class that rebuilds the network in cytoscape.
	 * and writes the results to the output file.
	 */
	public void run() {
		Consts.setCurDir("/home/bnet/liatperl/parfait/");
		// MapToAnnot.init(); // Initialize the annotations of the proteins // TEMP TODO not needed - delete.
		try {
			while(!interrupted) {
				_taskMonitor.setPercentCompleted(-1);
				System.out.println("before building the netowrk");
				// Building the network
				HashMap<String, Gene> geneMap = new HashMap<String, Gene>();	

				SimpleWeightedGraph<String, DefaultWeightedEdge> net = null;
				net = CytoscapeNetFactory.generate(geneMap, _cyNet);
				System.out.println("outputfile:"+_outputFile+ " cutoff:"+_cutoff+" alpha:"+_alpha+" algorithm:"+_algorithm);
				// Loading annotations
				AnnotationsManager a = new AnnotationsManager();
				// TEMP TODO - set annotations file automatically.
				int annotatedGenesInGenome = a.load("./data/gene_ontology_edit.obo", "./data/gene_assoc_human.txt", geneMap);
				// alter the edges weights.
				_taskMonitor.setStatus(GO_SIMILAR);
				if (interrupted) {
					return;
				}
				WeightFunction alterWeight = new WeightFunction(geneMap,a);
				//System.out.println("network before alteration:");
				//printNetwork(net);
				alterWeight.alterNetWeight(net,_alpha);
				//System.out.println("network after alteration:");
				//printNetwork(net);
				//System.out.println(net.toString());
				// Init clustering algorithm(s).
				_taskMonitor.setPercentCompleted(30);
				//_taskMonitor.setStatus(GENERATE_CLUSTERS);

				LinkedList<Cluster> clusters = null;
				if (_algorithm == MCL) {
					MarkovClustring markov = new MarkovClustring();
					clusters = markov.getClusters(net, geneMap);
				}
				else { // It is network blast.
					NetworkBlast netBlast = new NetworkBlast();
					clusters = netBlast.getClusters(net, geneMap);
				}
				_taskMonitor.setPercentCompleted(50);
				_taskMonitor.setStatus(ASSIGN_ANNOT);
				addAnnotToClusters(clusters, annotatedGenesInGenome);
				if (interrupted) {
					return;
				}
				// remove and add proteins to the existing clusters.
				ClusterListManipulator enrichLegalizer = new CLMEnrichmentLegalizer(a, net);
				ClusterListManipulator neighborsAdder = new CLMAddUnclusteredNeighbours(geneMap, a, net);
				// remove proteins
				enrichLegalizer.manipulate(clusters);
				// add proteins.
				neighborsAdder.manipulate(clusters);

				BuildClusterNet.colorNetwork(geneMap, clusters);
				writeOutputFile(clusters);
				_taskMonitor.setStatus(DONE_MESSAGE);
				_taskMonitor.setPercentCompleted(100);
				Thread.sleep(1000);
				break;
			} 
		} catch (Exception ex) {
			ex.printStackTrace();
			_taskMonitor.setException(ex, ex.getMessage());
			return;
		}
	}

	/**
	 * Writes the clusters with their annotation to the output file.
	 * @param clusters the clusters found by the clustering algorithms.
	 */
	private void writeOutputFile(LinkedList<Cluster> clusters) {
		FileWriter write;
		Iterator<Gene> iter;
		Gene gene;
		String toWrite;
		boolean isfirstCluster = true ;
		try {
			write = new FileWriter(_outputFile);
		} catch (IOException ioe) {
			GUIException.displayErrorMessage("Can't write to file "+ _outputFile);
			return;
		}

		// TEMP TODO - Oded: Liat - make sure the output file format is correct. Looks strange and messy.
		try {
			for (Cluster clus: clusters) {
				iter = clus.iterator();
				write.write(clus.getAnnotation()+"\t");
				while (iter.hasNext()) {
					if (! isfirstCluster) {
						write.write(" ");
					} else {
						isfirstCluster = false;
					}
					gene = iter.next();
					toWrite = gene.getName();
					write.write(toWrite);
				}
				write.write("\n");

			}
			write.close();
		} catch (IOException ieo) {
			GUIException.displayErrorMessage("Can't write to file:"+ _outputFile);
			return;
		}
		System.out.println("Finished the program");
	}

	public static void printNetwork(SimpleWeightedGraph<String, DefaultWeightedEdge> net) {
		for (DefaultWeightedEdge edge: net.edgeSet()) {
			System.out.println(net.getEdgeSource(edge)+"\t"+net.getEdgeWeight(edge)+"\t"+net.getEdgeTarget(edge));
		}

	}


	public String getTitle() {
		return INITIAL_STATUS;
	}


	public void halt() {
		interrupted = true;
		_taskMonitor.setStatus("Canceling task...");
		return;
	}


	public void setTaskMonitor(TaskMonitor arg0)
			throws IllegalThreadStateException {
		_taskMonitor = arg0;		
	}
}
